﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
using ProtocoloPropietario;
using System.Security.Cryptography;
using System.Net;

namespace Servidor
{
    class ClientHandlerTcp
    {
        private TcpClient ClientSocket;

        public ClientHandlerTcp(TcpClient ClientSocket)
        {
            this.ClientSocket = ClientSocket;
        }

        public void Process(Object O)
        {
            SharedState SharedStateObj = (SharedState)O;

            // Incoming data from the client.
            bool bQuit = false;

            // Data buffer for incoming data.
            byte[] paquete;

            NetworkStream networkStream = ClientSocket.GetStream();
            ClientSocket.ReceiveTimeout = 100; // 1000 miliseconds

            paquete = new byte[ClientSocket.ReceiveBufferSize];
            try
            {
                int BytesRead = networkStream.Read(paquete, 0, (int)ClientSocket.ReceiveBufferSize);
                if (BytesRead > 0)
                {
                    //data = Encoding.ASCII.GetString(paquete, 0, BytesRead);
                    MensajeProtocolo msg = MensajeProtocolo.ObtenerMensajeProtocolo(paquete);

                    // Show the data on the console.
                    Console.WriteLine("Mensaje recibido: {0}", msg.Cmd.ToString());

                    //Verifico protocolo y tomo accion
                    // y luego escribo respuesta
                    switch (msg.Cmd)
                    {
                        case Protocolo.Comando.SERVIDOR_CONEXION_SUBIDA:
                            if (msg.Header.Equals(Protocolo.Header.RES))
                            {
                                string fileName = msg.InfoDatos()[0];
                                //Obtener el archivo para mandar y ponerlo en el msg
                                bQuit = SubirArchivoTCP(fileName);
                            }
                            break;
                        default:
                            bQuit = true;
                            break;
                    }
                }
            }
            catch (IOException) { } // Timeout
            catch (SocketException)
            {
                bQuit = true;
                Console.WriteLine("Conection is broken!");
            }


            //Schedule task again 
            if (SharedStateObj.ContinueProcess && !bQuit)
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.Process), SharedStateObj);
            else
            {
                networkStream.Close();
                ClientSocket.Close();

                // Deduct no. of clients by one
                Interlocked.Decrement(ref SharedStateObj.NumberOfClients);

                Console.WriteLine("A client left, number of connections is {0}", SharedStateObj.NumberOfClients);
            }

            // Signal main process if this is the last client connections main thread requested to stop.
            if (!SharedStateObj.ContinueProcess && SharedStateObj.NumberOfClients == 0) SharedStateObj.Ev.Set();

        }

        public bool SubirArchivoTCP(string fileName)
        {
            try
            {
                NetworkStream networkStream = ClientSocket.GetStream();
                ClientSocket.ReceiveTimeout = 100; // 1000 miliseconds
                string ruta = (Program.rutaCarpetaArchivos +"\\" + fileName);
                FileInfo fi = new FileInfo(ruta);
                long tamanioArchivo = fi.Length;

                string msjReq = fi.Length.ToString();
                MensajeProtocolo msjProtocoloReq = new MensajeProtocolo(Protocolo.Header.RES, Protocolo.Comando.SERVIDOR_SUBIDA_INFO, msjReq.Length, msjReq);
                byte[] paqueteReq = msjProtocoloReq.ConvertirAByte();
                networkStream.Write(paqueteReq, 0, paqueteReq.Length);

                //Aca tengo que leer que le llego el info subida

                //Mandamos Archivo
                FileStream fileASubir = new FileStream(ruta, FileMode.Open, FileAccess.Read);

                string clientIP = ((IPEndPoint)ClientSocket.Client.RemoteEndPoint).Address.ToString();
                using (fileASubir)
                {
                    tamanioArchivo = fileASubir.Length;
                    fileASubir.Position = 0;
                    //lee segun el tamanio del protocolo
                    long tamDatos = 10000;
                    byte[] buff = new byte[tamDatos];
                    int readCount = fileASubir.Read(buff, 0, (int)tamDatos);
                    //Thread.Sleep(250);
                    ClientSocket.ReceiveTimeout = 100;
                    Console.WriteLine("Enviando al servidor " + clientIP + " el archivo " + Path.GetFileName(ruta) + ".....( 0% )");
                    long cantBytes = 0;
                    int n = 0;
                    while (readCount > 0)
                    {
                        MensajeProtocolo msjAEnviar = new MensajeProtocolo(Protocolo.Header.REQ, Protocolo.Comando.SERVIDOR_SUBIDA, readCount, buff);
                        byte[] protocoloAEnviar = msjAEnviar.ConvertirAByte();

                        networkStream.Write(protocoloAEnviar, 0, protocoloAEnviar.Length);
                        cantBytes += buff.Length;

                        byte[] buffRespuesta = new byte[ClientSocket.ReceiveBufferSize];
                        networkStream.Read(buff, 0, (int)ClientSocket.ReceiveBufferSize);
                        MensajeProtocolo msjRes = MensajeProtocolo.ObtenerMensajeProtocolo(buffRespuesta);
                        string ok = msjRes.InfoDatos()[0];

                        if (ok.Equals(Protocolo.OK))
                        {
                            if (tamanioArchivo < cantBytes + buff.Length && (tamanioArchivo - cantBytes) > 0)
                            {
                                buff = new byte[tamanioArchivo - cantBytes];
                                tamDatos = (int)tamanioArchivo - cantBytes;
                            }
                            else
                            {
                                buff = new byte[tamDatos];
                            }
                            readCount = fileASubir.Read(buff, 0, (int)tamDatos);
                        }
                        else
                        {
                            cantBytes -= buff.Length;
                        }
                        n++;
                        if (n == 5) n = 0;
                        if (n == 0) Console.WriteLine("Enviando al cliente " + clientIP + " el archivo " + Path.GetFileName(ruta) + ".....( " + cantBytes * 100 / tamanioArchivo + "% )");
                    }
                    Console.WriteLine("Enviando al servidor " + clientIP + " el archivo " + Path.GetFileName(ruta) + ".....( 100% )");
                    fileASubir.Close();
                }
                
                return true;
            }
            catch (FileNotFoundException)
            {
                return false;
            }
            catch (SocketException)
            {
                return false;
            }
        }
    }
}
